﻿using CloudStorageLight.Core;
using CloudStorageLight.Core.Web;
using CloudStorageLight.SharePointWeb.Models;
using Microsoft.SharePoint.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;

namespace CloudStorageLight.SharePointWeb.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        //
        // GET: /Account/Login
        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }

        //
        // POST: /Account/Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Login(string cloudAccount, string email, string password, string returnUrl, string clientToken)
        {
            var domain = BlobService.GetCurrentDomain();
            if (string.IsNullOrEmpty(email))
            {
                ModelState.AddModelError("email", ViewMessages.MsgRequired);
            }
            if (string.IsNullOrEmpty(password))
            {
                ModelState.AddModelError("password", ViewMessages.MsgRequired);
            }
            if (ModelState.IsValid)
            {
                CloudAccount targetCloudAccount;
                if (!string.IsNullOrEmpty(cloudAccount))
                {
                    targetCloudAccount = CloudAccount.Load(SystemSettings.Instance.BlobStorage, cloudAccount);
                    if (targetCloudAccount != null)
                    {
                        domain = cloudAccount;
                    }
                    else
                    {
                        this.ModelState.AddModelError("cloudAccount", ViewMessages.MsgNotFoundCloudAccount);
                    }
                }
                else
                {
                    targetCloudAccount = BlobService.GetCurrentCloudAccount();
                }


                if (BasicAuthenticateUtil.AuthenticateUser(email, password, clientToken, targetCloudAccount))
                {
                    if (string.IsNullOrEmpty(returnUrl))
                    {
                        returnUrl = DomainRouteUrl("files", new { domain = domain, id = "/" });
                    }
                    returnUrl += (returnUrl.Contains('?') ? "&" : "?") + "SPHostUrl=" + HttpUtility.UrlEncode(targetCloudAccount.SpHostUrl) + "&";

                    if (string.IsNullOrEmpty(clientToken))
                    {
                        var registClientUrl = DomainRouteUrl("account", new { action = "RegisterClientToken", domain = targetCloudAccount.AccountName });
                        return Redirect(registClientUrl);
                    }
                    else
                    {
                        return Redirect(returnUrl);
                    }
                }
                else
                {
                    this.ModelState.AddModelError("", ViewMessages.MsgInvalidLoginUserPass);
                }
            }

            // ここで問題が発生した場合はフォームを再表示します
            return View();
        }

        public ActionResult RegisterClientToken(string returnUrl)
        {
            if (!Url.IsLocalUrl(returnUrl))
            {
                var domain = BlobService.GetCurrentDomain();
                var targetCloudAccount = BlobService.GetCurrentCloudAccount();
                returnUrl = DomainRouteUrl("files", new { domain = domain, id = "/" });
                returnUrl += (returnUrl.Contains('?') ? "&" : "?") + "SPHostUrl=" + HttpUtility.UrlEncode(targetCloudAccount.SpHostUrl) + "&";
            }
            ViewBag.ReturnUrl = returnUrl;

            var ctoken = new ClientToken { ExpireDate = DateTime.MaxValue, IsuueDate = DateTime.UtcNow, InitIPAddress = WebUtil.GetIPAddress(), UserName = User.Identity.Name };
            ViewBag.ClientToken = ctoken.ToToken();
            return View();
        }

        private string DomainRouteUrl(string routeName, object routeData)
        {
            if (SystemSettings.Instance.MultiDomain)
            {
                return Url.RouteUrl(routeName, routeData);
            }
            else
            {
                RouteValueDictionary newRouteData = new RouteValueDictionary();
                foreach (System.ComponentModel.PropertyDescriptor property in System.ComponentModel.TypeDescriptor.GetProperties(routeData))
                {
                    if (property.Name.ToLower() == "domain") continue;
                    newRouteData.Add(property.Name, property.GetValue(routeData));
                }
                return Url.RouteUrl(routeName, newRouteData);
            }
        }

        private bool IsSPAuthenticated()
        {
            Uri redirectUrl;
            return SharePointContextProvider.CheckRedirectionStatus(this.HttpContext, out redirectUrl) == RedirectionStatus.Ok;
        }

        [AllowAnonymous]
        public ActionResult CreateCloudAccount()
        {
            if (!SystemSettings.Instance.CanCreateCloudAccountOnline) throw new BlobException(BlobExceptionCode.NotAuthorization);
            var vm = new CloudAccountVM();
            if (IsSPAuthenticated())
            {
                vm.IsSPAuthenticated = true;
                vm.SpHostUrl = SharePointContext.GetSPHostUrl(this.Request).AbsoluteUri;
                var cloudAccount = CloudAccount.LoadBySpHostUrl(SystemSettings.Instance.BlobStorage, vm.SpHostUrl);
                if (cloudAccount != null) throw new InvalidOperationException(ViewMessages.MsgAlreadyExistsCloudAccount);
                vm.EMailAddress = BasicAuthenticateUtil.GetCurrentUser().Name;
            }
            else
            {
                vm.IsSPAuthenticated = false;
            }
            ViewData["MenuVisibility"] = false;
            return View(vm);
        }

        [HttpPost]
        [AllowAnonymous]
        public ActionResult CreateCloudAccount(CloudAccountVM vm)
        {
            if (!SystemSettings.Instance.CanCreateCloudAccountOnline) throw new BlobException(BlobExceptionCode.NotAuthorization);


            if (string.IsNullOrEmpty(vm.Password))
            {
                ModelState.AddModelError("Password", ViewMessages.MsgRequired);
            }
            if (string.IsNullOrEmpty(vm.EMailAddress))
            {
                ModelState.AddModelError("EMailAddress", ViewMessages.MsgRequired);
            }

            if (!WebUtil.ValidUrl(vm.SpHostUrl))
            {
                ModelState.AddModelError("SpHostUrl", ViewMessages.MsgInvalidUrlFormat);
            }
            //入力エラー用のデータを再設定
            if (IsSPAuthenticated())
            {
                vm.IsSPAuthenticated = true;
                vm.SpHostUrl = SharePointContext.GetSPHostUrl(this.Request).AbsoluteUri;
                vm.EMailAddress = BasicAuthenticateUtil.GetCurrentUser().Name;
            }
            else
            {
                vm.IsSPAuthenticated = false;
            }

            vm.IsSPAuthenticated = IsSPAuthenticated();
            if (SystemSettings.Instance.MultiDomain)
            {
                if (string.IsNullOrEmpty(vm.CloudAccount))
                {
                    this.ModelState.AddModelError("CloudAccount", ViewMessages.MsgPleaseInputCloudAccount);
                }
                else
                {
                    vm.CloudAccount = vm.CloudAccount.Trim();

                    if (vm.CloudAccount.Length < 5 || vm.CloudAccount.Length > 15 || !System.Text.RegularExpressions.Regex.IsMatch(vm.CloudAccount, "^[a-z][a-z0-9]*$"))
                    {
                        this.ModelState.AddModelError("CloudAccount", ViewMessages.MsgPleaseInputCloudAccountAlphaNumber15);
                    }
                    vm.CloudAccount = vm.CloudAccount.ToLower();
                }

            }
            else
            {
                vm.CloudAccount = "";
            }
 
            CloudAccount account = null; 

            if (this.ModelState.IsValid)
            {
                account = CloudAccount.Load(SystemSettings.Instance.BlobStorage, vm.CloudAccount);
                if (account != null)
                {
                    this.ModelState.AddModelError("CloudAccount", ViewMessages.MsgAlreadyExists);
                    this.ModelState.AddModelError("", ViewMessages.MsgAlreadyExistsCloudAccount);
                }
            }

            if (this.ModelState.IsValid)
            {
                if (!IsSPAuthenticated())
                {
                    vm.SpHostUrl = new Uri(vm.SpHostUrl).AbsoluteUri; //正規化

                    account = CloudAccount.LoadBySpHostUrl(SystemSettings.Instance.BlobStorage, vm.SpHostUrl);
                    if (account != null)
                    {
                        this.ModelState.AddModelError("SpHostUrl", ViewMessages.MsgAlreadyExists);
                    }


                    //SharePoint認証で無い場合
                    var securePassword = new SecureString();
                    foreach (char c in vm.Password)
                    {
                        securePassword.AppendChar(c);
                    }
                    var onlineCredentials = new SharePointOnlineCredentials(vm.EMailAddress, securePassword);

                    using (var ctx = new ClientContext(vm.SpHostUrl))
                    {
                        ctx.Credentials = onlineCredentials;
                        try
                        {
                            var spUser = ctx.Web.CurrentUser;
                            ctx.Load(spUser, user => user.Email, user => user.Groups);
                            ctx.ExecuteQuery();
                        }
                        catch
                        {
                            this.ModelState.AddModelError("SpHostUrl", ViewMessages.MsgCannotLoginSpSite);
                        }
                    }
                }
                else
                {
                    //SharePoint認証の場合
                    vm.EMailAddress = BasicAuthenticateUtil.GetCurrentUser().Name;
                    vm.SpHostUrl = SharePointContext.GetSPHostUrl(this.Request).AbsoluteUri;
                    
                    var spContext = SharePointContextProvider.Current.GetSharePointContext(System.Web.HttpContext.Current);
                    spContext.SPHostUrl = new Uri(vm.SpHostUrl);
                    using (var ctx = spContext.CreateUserClientContextForSPHost())
                    {
                        try
                        {
                            var spUser = ctx.Web.CurrentUser;
                            ctx.Load(spUser, user => user.Email, user => user.Groups);
                            ctx.ExecuteQuery();
                        }
                        catch
                        {
                            this.ModelState.AddModelError("SpHostUrl", ViewMessages.MsgCannotLoginSpSite);
                        }
                    }
                }

                if (this.ModelState.IsValid)
                {
                    var cloudAccount = new CloudAccount(vm.CloudAccount) { 
                        AccountType = "SharePoint",
                        SpHostUrl = vm.SpHostUrl, 
                        EMailAddress = vm.EMailAddress,
                        StorageLimitSize = SystemSettings.Instance.DefaultStorageLimitSize,
                    };
                    cloudAccount.Save(SystemSettings.Instance.BlobStorage);
                    var queryString = new RouteValueDictionary();
                    foreach (var item in this.Request.QueryString.AllKeys)
                    {
                        queryString.Add(item??"", this.Request[item]);
                    }
                    queryString["domain"] = vm.CloudAccount;
                    queryString["SPHostUrl"] = vm.SpHostUrl;
                    queryString["Created"] = "1";

                    var ticket = new FormsAuthenticationTicket(0, vm.EMailAddress, DateTime.UtcNow, DateTime.UtcNow.Add(FormsAuthentication.Timeout), false, vm.CloudAccount);
                    HttpCookie cookie = new HttpCookie(
                        FormsAuthentication.FormsCookieName,
                        FormsAuthentication.Encrypt(ticket));
                    System.Web.HttpContext.Current.Response.Cookies.Add(cookie);

                    return RedirectToAction("Index", "Home", queryString);
                }
            }
            return View(vm);
        }


        [AllowAnonymous]
        public ActionResult IsAuthenticated()
        {
            return Json(HttpContext.User.Identity.IsAuthenticated, JsonRequestBehavior.AllowGet);
        }

        [AllowAnonymous]
        public ActionResult GetCloudAccount()
        {
            if (!HttpContext.User.Identity.IsAuthenticated) return Json(null);
            var account = BlobService.GetCurrentCloudAccount();
            if (account == null) return Json(null);
            return Json(new { Account = account.AccountName, account.AccountType, account.EMailAddress, account.SpHostUrl }, JsonRequestBehavior.AllowGet);
        }


    }
}